<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        // 数组方法总结
        let fruits = ['Apple', 'Banana']

        // 通过索引访问数组元素
        let fruit = fruits[0]
        console.log(fruit)

        // 数组的遍历

        fruits.forEach(function (item, index, array) {
            console.log(item, index);
        });

        // 添加元素到数组末尾 push ，返回值是新数组的长度
        let newLength = fruits.push('Orange')
        console.log('length:' + newLength + "    newArray::" + fruits)

        //  删除数组末尾的元素 pop 返回删除的元素
        let poped = fruits.pop()
        console.log('pop::' + poped)

        // 删除数组头部的元素 shift 返回删除的数组
        let shifted = fruits.shift()
        console.log("shifted::" + shifted)

        // 在元素头部添加元素 返回新长度
        let newLength2 = fruits.unshift('Grape')
        console.log("newLengt2::" + newLength2)
        console.log(fruits)

        //找出某个元素在数组中的索引
        fruits.push('Mongo')
        fruits.push('BingGo')
        let index = fruits.indexOf('Mongoo')
        console.log('index:: ' + index)

        // 通过索引删除某个元素
        let removeItem = fruits.splice(1, 1)
        console.log(removeItem)

        // 从一个索引位置删除多个元素
        let removeItems = fruits.splice(1, 2)
        console.log(removeItems)


        // 复制一个数组 
        fruits.push('Yali')
        fruits.unshift('XiangGua')
        var shallowCopy = fruits.slice();
        console.log(shallowCopy)

        // 赋值元素下标超出原长度范围
        shallowCopy[5] = "shallow"
        console.log(shallowCopy)
        console.log(Object.keys(shallowCopy))

        // 给length 赋值 比实际大
        shallowCopy.length = 10;
        console.log(shallowCopy)
        console.log(Object.keys(shallowCopy))

        // 给原数组length 赋值一个更小的值 会删除一些对应的元素
        shallowCopy.length = 3
        console.log(shallowCopy)
        console.log(Object.keys(shallowCopy))

        //copyWithin() 方法浅复制数组的一部分到同一数组中的另一个位置，并返回它，不会改变原数组的长度。
        const array1 = ['a', 'b', 'c', 'd', 'e'];
        // copy to index 0 the element at index 3
        // ["d", "b", "c", "d", "e"] 赋值index 为3的元素 到0 的位置
        console.log(array1.copyWithin(0, 3, 4))
        // copy to index 1 all elements from index 3 to the end
        // ["d", "d", "e", "d", "e"] // 复制index 3 到末尾的元素到 index为1的位置  从1 开始 de 替换 dc ==> ddede
        console.log(array1.copyWithin(1, 3));

        // Array.prototype.fill()  将数组中指定区间的所有元素的值，都替换成某个固定的值。
        const arrayfill = [1, 2, 3, 4];
        console.log(arrayfill.fill(0, 2, 4))

        // reverse() 方法将数组中元素的位置颠倒，并返回该数组。数组的第一个元素会变成最后一个，数组的最后一个元素变成第一个。该方法会改变原数组。
        const arrayReverse = ['one', 'two', 'three'];
        console.log('arrayReverse:', arrayReverse.reverse());

        // sort() 方法用原地算法对数组的元素进行排序，并返回数组。默认排序顺序是在将元素转换为字符串，然后比较它们的UTF-16代码单元值序列时构建的
        const months = ['March', 'Jan', 'Feb', 'Dec'];
        months.sort();
        console.log(months); // ["Dec", "Feb", "Jan", "March"]
        const arraySort = [1, 30, 4, 21, 100000];
        arraySort.sort();
        console.log(arraySort); // [1, 100000, 21, 30, 4]

        //arr.sort([compareFunction])可以放置函数
        // 返回值 是排序后的数组 
        function compareNumbers(a, b) {
            // a-b< 0 a在前  a-b > 0 a 在后 
            return a - b;
        }
        var numbers = [4, 2, 5, 1, 3];
        numbers.sort(function (a, b) {
            return a - b;
        });
        console.log(numbers);

        // 也可以写成：
        var numbers = [4, 2, 5, 1, 3];
        numbers.sort((a, b) => a - b);
        console.log(numbers);
        var items = [{
                name: 'Edward',
                value: 21
            },
            {
                name: 'Sharpe',
                value: 37
            },
            {
                name: 'And',
                value: 45
            },
            {
                name: 'The',
                value: 12
            },
            {
                name: 'Magnetic'
            },
            {
                name: 'Zeros',
                value: 37
            }
        ];

        console.log(items)
        // sort by value
        items.sort(function (a, b) {
            return (a.value - b.value)
        });

        console.log("items：：：", items)
        // sort by name
        items.sort(function (a, b) {
            var nameA = a.name.toUpperCase(); // ignore upper and lowercase
            var nameB = b.name.toUpperCase(); // ignore upper and lowercase
            if (nameA < nameB) {
                return -1;
            }
            if (nameA > nameB) {
                return 1;
            }
            // names must be equal
            return 0;
        });
        console.log(items)

        // splice() 方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。
        // array.splice(start[, deleteCount[, item1[, item2[, ...]]]])
        // start 指定修改的开始位置 
        // deleteCount 表示要移除数组的个数
        // item 要添加进数组的元素
        var myFish = ["angel", "clown", "mandarin", "sturgeon"];
        var removed = myFish.splice(2, 0, "drum");
        console.log("removed :: " + removed + "  :: " + myFish)
        // 运算后的 myFish: ["angel", "clown", "drum", "mandarin", "sturgeon"]
        // 被删除的元素: [], 没有元素被删除

        var myFish = ['angel', 'clown', 'drum', 'mandarin', 'sturgeon'];
        var removed = myFish.splice(3, 1);
        console.log("removed :: " + removed + "  :: " + myFish)
        // 运算后的 myFish: ["angel", "clown", "drum", "sturgeon"]
        // 被删除的元素: ["mandarin"]

        var myFish = ['angel', 'clown', 'trumpet', 'sturgeon'];
        var removed = myFish.splice(0, 2, 'parrot', 'anemone', 'blue');
        console.log("removed :: " + removed + "  :: " + myFish)
        // 运算后的 myFish: ["parrot", "anemone", "blue", "trumpet", "sturgeon"]
        // 被删除的元素: ["angel", "clown"]

        // 从倒数开始删除元素
        //
        var myFish = ['angel', 'clown', 'mandarin', 'sturgeon'];
        var removed = myFish.splice(-2, 1);
        console.log("removed :: " + removed + "  :: " + myFish)
        // 运算后的 myFish: ["angel", "clown", "sturgeon"]
        // 被删除的元素: ["mandarin"]

        var myFish = ['angel', 'clown', 'mandarin', 'sturgeon'];
        var removed = myFish.splice(2);
        console.log("removed :: " + removed + "  :: " + myFish)
        // 运算后的 myFish: ["angel", "clown"]
        // 被删除的元素: ["mandarin", "sturgeon"]


        // 访问方法的一些实践
        // concat() 方法用于合并两个或多个数组。此方法不会更改现有数组，而是返回一个新数组。
        const array1ct = ['a', 'b', 'c'];
        const array2ct = ['d', 'e', 'f'];
        const array3ct = array1ct.concat(array2ct);

        console.log(array3ct);
        // expected output: Array ["a", "b", "c", "d", "e", "f"]

        //includes() 方法用来判断一个数组是否包含一个指定的值，根据情况，如果包含则返回 true，否则返回false。
        const array1Include = [1, 2, 3];

        console.log(array1Include.includes(2));
        // expected output: true

        const pets = ['cat', 'dog', 'bat'];

        console.log(pets.includes('cat'));
        // expected output: true

        console.log(pets.includes('at'));
        // expected output: false
        // arr.includes(valueToFind[, fromIndex]) valueToFind 需要查找的值  fromIndex 查找开始的为孩子 如果是负数 则从末尾向前查找
        [1, 2, 3].includes(2); // true
        [1, 2, 3].includes(4); // false
        [1, 2, 3].includes(3, 3); // false
        [1, 2, 3].includes(3, -1); // true
        [1, 2, NaN].includes(NaN); // true
        // fromIndex  大于等于数组长度 则返回false
        // 作为通用方法的include 
        // includes() 方法有意设计为通用方法。它不要求this值是数组对象，所以它可以被用于其他类型的对象 (比如类数组对象)。下面的例子展示了 在函数的 arguments 对象上调用的 includes() 方法。
        (function () {
            console.log([].includes.call(arguments, 'a')); // true
            console.log([].includes.call(arguments, 'd')); // false
        })('a', 'b', 'c');

        // join() 方法将一个数组（或一个类数组对象）的所有元素连接成一个字符串并返回这个字符串。如果数组只有一个项目，那么将返回该项目而不使用分隔符。
        const elements = ['Fire', 'Air', 'Water'];

        console.log(elements.join());
        console.log(elements.join(','));
        // expected output: "Fire,Air,Water"

        console.log(elements.join(''));
        // expected output: "FireAirWater"

        console.log(elements.join('-'));
        // expected output: "Fire-Air-Water"

        // 下面的示例将连接类数组对象（arguments），通过在Array.prototype.join上调用Function.prototype.call。

        function f(a, b, c) {
            var s = Array.prototype.join.call(arguments);
            console.log(s); // '1,a,true'
        }
        f(1, 'a', true);

        // slice() 方法返回一个新的数组对象，这一对象是一个由 begin 和 end 决定的原数组的浅拷贝（包括 begin，不包括end）。原始数组不会被改变。
        const animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];

        console.log(animals.slice(2));
        // expected output: Array ["camel", "duck", "elephant"]

        console.log(animals.slice(2, 4));
        // expected output: Array ["camel", "duck"]

        console.log(animals.slice(1, 5));
        // expected output: Array ["bison", "camel", "duck", "elephant"]

        // toString() 返回一个字符串， 表示指定的数组及其元素。
        const array1Tostr = [1, 2, 'a', '1a'];
        console.log(array1Tostr.toString());
        // expected output: "1,2,a,1a"
        // 输出以上的结果 想到了jion() 方法默认

        // toLocaleString() 返回一个字符串表示数组中的元素。数组中的元素将使用各自的 toLocaleString 方法转成字符串，这些字符串将使用一个特定语言环境的字符串（例如一个逗号 ","）隔开。 没用过这个方法 感觉还挺神奇的
        const arrayTolocal = [1, 'a', new Date('21 Dec 1997 14:12:00 UTC')];
        const localeString = arrayTolocal.toLocaleString('en', {
            timeZone: 'UTC'
        });

        console.log(localeString);
        // expected output: "1,a,12/21/1997, 2:12:00 PM",
        // This assumes "en" locale and UTC timezone - your results may vary

        // indexOf()方法返回在数组中可以找到一个给定元素的第一个索引，如果不存在，则返回-1。
        // arr.indexOf(searchElement[, fromIndex])
        // indexOf 看语法和include()一样 只是一个返回找到的索引 一个直接返回true or false 


        // lastIndexOf() 方法返回指定元素（也即有效的 JavaScript 值或变量）在数组中的最后一个的索引，如果不存在则返回 -1。从数组的后面向前查找，从 fromIndex 处开始。
        // arr.lastIndexOf(searchElement[, fromIndex])
        // 语法和index 一样 ，只是找的最后的一个索引


        // 迭代方法 迭代方法

        // Array.prototype.forEach()  为数组中的每个元素执行一次回调函数。
        // 语法 arr.forEach(callback(currentValue [, index [, array]])[, thisArg]) 参数分别是当前值 当前索引 当前遍历数组 this 值
        // forEach 的弊端 是无法终止for 循环
        function logArrayElements(element, index, array) {
            // 直接把没有值的部分给跳过了
            console.log('a[' + index + '] = ' + element);
        }

        // 注意索引 2 被跳过了，因为在数组的这个位置没有项
        [2, 5, , 9].forEach(logArrayElements);
        // logs:
        // a[0] = 2
        // a[1] = 5
        // a[3] = 9

        // entries() 方法返回一个新的Array Iterator对象，该对象包含数组中每个索引的键/值对。
        const array1entries = ['a', 'b', 'c'];

        const iterator1 = array1entries.entries();
        console.log(iterator1)

        console.log(iterator1.next().value);
        // expected output: Array [0, "a"]

        console.log(iterator1.next().value);
        // expected output: Array [1, "b"]

        // every() 方法测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。

        const isBelowThreshold = (currentValue) => currentValue < 40;

        const array1Every = [1, 30, 39, 29, 10, 13];

        console.log(array1Every.every(isBelowThreshold));
        // expected output: true

        // Array.prototype.some() 如果数组中至少有一个元素满足测试函数，则返回 true，否则返回 false。
        const array = [1, 2, 3, 4, 5];

        // checks whether an element is even
        const even = (element) => element % 2 === 0;

        console.log(array.some(even));
        // expected output: true

        // filter() 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。 

        const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];

        const result = words.filter(word => word.length > 6);

        console.log(result);
        // expected output: Array ["exuberant", "destruction", "present"]

        // find() 方法返回数组中满足提供的测试函数的第一个元素的值。 否则返回 undefined。

        // findIndex()方法返回数组中满足提供的测试函数的第一个元素的索引。若没有找到对应元素则返回-1。

        // keys() 方法返回一个包含数组中每个索引键的Array Iterator对象。
        const array1keys = ['a', 'b', 'c'];
        const iterator = array1keys.keys();
        console.log(iterator)

        for (const key of iterator) {
            console.log(key, array1keys[key]);
            // console.log(array1keys[key])
        }

        // expected output: 0
        // expected output: 1

        // map() 方法创建一个新数组，其结果是该数组中的每个元素是调用一次提供的函数后的返回值。
        // 创建一个新数组
        const array1Map = [1, 4, 9, 16];

        // pass a function to map
        const map1 = array1Map.map(x => x * 2);

        console.log(map1);
        console.log(array1Map)
        // expected output: Array [2, 8, 18, 32]

        //因为map生成一个新数组，当你不打算使用返回的新数组却使用map是违背设计初衷的，请用forEach或者for-of替代。你不该使用map: A)你不打算使用返回的新数组，或/且 B) 你没有从回调函数中返回值。

        // 使用 map 重新格式化数组中的对象
        var kvArray = [{
                key: 1,
                value: 10
            },
            {
                key: 2,
                value: 20
            },
            {
                key: 3,
                value: 30
            }
        ];

        var reformattedArray = kvArray.map(function (obj) {
            var rObj = {};
            rObj[obj.key] = obj.value;
            return rObj;
        });

        // reformattedArray 数组为： [{1: 10}, {2: 20}, {3: 30}], 

        // kvArray 数组未被修改: 
        // [{key: 1, value: 10}, 
        //  {key: 2, value: 20}, 
        //  {key: 3, value: 30}]

        // 一般的map 用法
        //下面的例子演示如何在一个 String  上使用 map 方法获取字符串中每个字符所对应的 ASCII 码组成的数组：
        var map = Array.prototype.map
        var a = map.call("Hello World", function (x) {
            return x.charCodeAt(0);
        })
        // a的值为[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]

        // querySelectorAll 应用

        // 下面代码展示了如何去遍历用 querySelectorAll 得到的动态对象集合。在这里，我们获得了文档里所有选中的选项，并将其打印：
        // var elems = document.querySelectorAll('select option:checked');
        // var values = Array.prototype.map.call(elems, function (obj) {
        //     return obj.value;
        // });

        //Mapping 含 undefined的数组
        var numbers = [1, 2, 3, 4];
        var filteredNumbers = numbers.map(function (num, index) {
            if (index < 3) {
                return num;
            }
        });
        console.log("length::" + filteredNumbers.length + "===" + filteredNumbers)
        //index goes from 0,so the filterNumbers are 1,2,3 and undefined.
        // filteredNumbers is [1, 2, 3, undefined]
        // numbers is still [1, 2, 3, 4]

        // reduce()

        // const array1Reduce = [1, 2, 3, 4];
        // // 定义了回调函数函数
        // const reducer = (accumulator, currentValue) => accumulator + currentValue;

        // // 1 + 2 + 3 + 4
        // console.log(array1Reduce.reduce(reducer));
        // // expected output: 10

        // // 5 + 1 + 2 + 3 + 4
        // console.log(array1Reduce.reduce(reducer, 5));
        // expected output: 15

        // var maxCallback = (acc, cur) => Math.max(acc.x, cur.x);
        // var maxCallback2 = (max, cur) => Math.max(max, cur);

        // // reduce() 没有初始值
        // [{
        //     x: 2
        // }, {
        //     x: 22
        // }, {
        //     x: 42
        // }].reduce(maxCallback); // NaN
        // console.log([{
        //     x: 2
        // }, {
        //     x: 22
        // }, {
        //     x: 42
        // }].reduce(maxCallback))[{
        //     x: 2
        // }, {
        //     x: 22
        // }].reduce(maxCallback); // 22
        // [{
        //     x: 2
        // }].reduce(maxCallback); // { x: 2 }
        // [].reduce(maxCallback); // TypeError

        // // map/reduce; 这是更好的方案，即使传入空数组或更大数组也可正常执行
        // [{
        //     x: 22
        // }, {
        //     x: 42
        // }].map(el => el.x)
        //     .reduce(maxCallback2, -Infinity);



        var names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];

        var countedNames = names.reduce(function (allNames, name) {
            console.log("allNames::" + JSON.stringify(allNames) + "  name:: " + name)
            if (name in allNames) {
                allNames[name]++;
            } else {
                allNames[name] = 1;
            }
            return allNames;
        }, {});
        console.log(countedNames)
        // countedNames is:
        // { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }

        // 对于深浅拷贝log 打印测试
        const arr = [0, 2, {
            "name": "zhangsan"
        }]
        console.log('arr:', JSON.parse(JSON.stringify(arr)))
        arr[2].name = '李四'
        console.log("arr::", arr)
    </script>
</body>

</html>